package pkg

import (
	"fmt"

	"gonum.org/v1/gonum/graph/simple"
)

/*
Business（业务）

	└─ Module（模块）
	      └─ Host（主机）

关系：
Business → Module（1:N）
Module → Host（1:N）
Host 可属于多个 Module（M:N）
（例如共享主机、在资源池 / 容器环境里）

Business  --->  Module  ---> Host
*/
type Node = string

const (
	Business = "Business"
	Module   = "Module"
	Host     = "Host"
)

type CMDBNode struct {
	id   int64
	name string
	typ  Node
}

func (n CMDBNode) ID() int64    { return n.id }
func (n CMDBNode) UUID() string { return fmt.Sprintf("%s-%x-%s", n.typ, n.id, n.name) }

func NewNode(id int64, name, typ string) CMDBNode {
	return CMDBNode{id, name, typ}
}

func NewGraph() *simple.DirectedGraph {
	g := simple.NewDirectedGraph()

	// 创建业务
	b1 := NewNode(1, "GameBusiness", Business)
	b2 := NewNode(2, "PayBusiness", Business)

	// 创建模块
	m1 := NewNode(10, "Game-Login", Module)
	m2 := NewNode(11, "Game-Match", Module)
	m3 := NewNode(20, "Pay-Gateway", Module)

	// 创建主机
	h1 := NewNode(100, "10.0.0.1", Host)
	h2 := NewNode(101, "10.0.0.2", Host)
	h3 := NewNode(102, "10.0.0.3", Host)

	nodes := []CMDBNode{b1, b2, m1, m2, m3, h1, h2, h3}
	for _, n := range nodes {
		g.AddNode(n)
	}

	// 建立业务 -> 模块
	g.SetEdge(g.NewEdge(b1, m1))
	g.SetEdge(g.NewEdge(b1, m2))
	g.SetEdge(g.NewEdge(b2, m3))

	// 建立模块 -> 主机关系
	g.SetEdge(g.NewEdge(m1, h1))
	g.SetEdge(g.NewEdge(m1, h2))
	g.SetEdge(g.NewEdge(m2, h2)) // h2 在两个模块
	g.SetEdge(g.NewEdge(m3, h3))

	fmt.Println("图构建完成")
	return g
}

// 1. 查某个业务下所有主机

func HostsUnderBusiness(g *simple.DirectedGraph, bid int64) []CMDBNode {
	business := g.Node(bid)
	if business == nil {
		return nil
	}

	var hosts []CMDBNode

	// 遍历业务 → 模块
	modules := g.From(bid)
	for modules.Next() {
		module := modules.Node()

		// 遍历模块 → 主机
		hs := g.From(module.ID())
		for hs.Next() {
			h := hs.Node().(CMDBNode)
			if h.typ == Host {
				hosts = append(hosts, h)
			}
		}
	}
	return hosts
}

func HostsUnderModule(g *simple.DirectedGraph, mid int64) []CMDBNode {
	var hs []CMDBNode
	to := g.From(mid)
	for to.Next() {
		n := to.Node().(CMDBNode)
		if n.typ == Host {
			hs = append(hs, n)
		}
	}
	return hs
}

//2. 查某台主机属于哪些 模块 / 业务

func ModulesOfHost(g *simple.DirectedGraph, hid int64) []CMDBNode {
	host := g.Node(hid)
	if host == nil {
		return nil
	}

	var modules []CMDBNode
	// 遍历所有节点，找指向 host 的边
	it := g.Nodes()
	for it.Next() {
		n := it.Node()
		if g.HasEdgeFromTo(n.ID(), hid) {
			mod := n.(CMDBNode)
			if mod.typ == "Module" {
				modules = append(modules, mod)
			}
		}
	}
	return modules
}

func BusinessOfHost(g *simple.DirectedGraph, hid int64) []CMDBNode {
	modules := ModulesOfHost(g, hid)
	var biz []CMDBNode

	for _, m := range modules {
		it := g.Nodes()
		for it.Next() {
			n := it.Node()
			if g.HasEdgeFromTo(n.ID(), m.ID()) {
				b := n.(CMDBNode)
				if b.typ == "Business" {
					biz = append(biz, b)
				}
			}
		}
	}
	return biz
}

func HostMultipleModules(g *simple.DirectedGraph) []CMDBNode {
	var result []CMDBNode
	it := g.Nodes()
	for it.Next() {
		node := it.Node().(CMDBNode)
		if node.typ != "Host" {
			continue
		}

		modules := ModulesOfHost(g, node.id)
		if len(modules) > 1 {
			result = append(result, node)
		}
	}
	return result
}
